import tkinter
import tkinter.ttk
import tkinter.scrolledtext
import tkinter.messagebox
import tkinter.filedialog
import socket
import re
import os
import os.path
import struct
import threading
import time
from time import sleep
import tkinter.font as tkFont

from PIL import ImageGrab, Image, ImageTk



def openimage(png,w,h):
      # 主界面背景图片
    im = Image.open(png)
    photo = ImageTk.PhotoImage(im)
    out = im.resize((w, h), Image.ANTIALIAS)  # resize image with high-quality
    out.save(png)
    im = Image.open(png)
    photo = ImageTk.PhotoImage(im)
    return photo

##=====================================界面布局=======================-=========##
root = tkinter.Tk(screenName='用户界面')  # 创建应用程序窗口
root.title('课堂教学管理系统')
root.geometry('320x150+500+300')
root.resizable(False, False)  # 不允许改变窗口大小


##=================================界面美化处理============================##
png = './picture/1.jpg'
photo = openimage(png,320,150)
Lab= tkinter.Label(root,compound='center',image= photo,width=320,height=150)
Lab.pack()#设置主界面

backtype='#F5F5F5'                      #按钮背景颜色
fonttype=("黑体",9,tkFont.BOLD)           #按钮字体类型
##=====================================界面美化处理结束======================##




xuehao = tkinter.StringVar(root)
xuehao.set('')
xingming = tkinter.StringVar(root)
xingming.set('')
server_IP = tkinter.StringVar(root)
server_IP.set('192.168.0.107')  # 默认服务器地址

photo1=openimage('./picture/11.png',60,20)
labelXuehao = tkinter.Label(root, image=photo1,text='学号：',compound='center', justify=tkinter.RIGHT)
labelXuehao.place(x=10, y=5 ,width=60, height=20)  # 将标签放到窗口上


entryXuehao = tkinter.Entry(root, width=150,  # 创建文本框
                            textvariable=xuehao)  # 同时设置关联的变量
entryXuehao.place(x=100, y=5, width=150, height=20)
photo2=openimage('./picture/12.png',60,20)
labelXingming = tkinter.Label(root, text='姓名：',compound='center', justify=tkinter.RIGHT,image= photo2)

labelXingming.place(x=10, y=30, width=60, height=20)

entryXingming = tkinter.Entry(root, width=150,textvariable=xingming)
entryXingming.place(x=100, y=30, width=150, height=20)

labelServerIP = tkinter.Label(root, text='服务器IP：', justify=tkinter.RIGHT, width=80)
labelServerIP['background']='#FFFFFF'
labelServerIP.place(x=10, y=60, width=80, height=20)
entryServerIP = tkinter.Entry(root, width=150, textvariable=server_IP)
entryServerIP.place(x=100, y=60, width=150, height=20)
##===========================================界面布局代码结束=======================##






##===============================点名功能代码=============================================##
def buttonOKClick():  # 登录按钮事件处理函数
    xuehao = entryXuehao.get()  # 获取学号
    xingming = entryXingming.get()  # 获取姓名
    serverIP = entryServerIP.get()
    if not re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', serverIP):
        tkinter.messagebox.showerror('很抱歉', '服务器IP地址不合法')
        return
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.connect((serverIP, 30500))
    except Exception as e:
        tkinter.messagebox.showerror('很抱歉', '现在不是点名时间')
        return

    # 获取客户端MAC地址，使用MAC+IP保证每台计算机每节课只能点名一次
    import uuid
    node = uuid.getnode()
    macHex = uuid.UUID(int=node).hex[-12:]
    mac = []
    for i in range(len(macHex))[::2]:
        mac.append(macHex[i:i + 2])
    mac = ''.join(mac)

    sock.sendall(','.join((xuehao, xingming, mac)).encode())

    data = sock.recv(1024).decode()


    if data.lower() == 'ok':
        # 点名成功
        sock.close()

        # 保存学号、姓名和服务器IP地址，方便下次自动填写信息


        tkinter.messagebox.showinfo('恭喜', xuehao + ',' + xingming + '  报到点名成功')
        return
    elif data.lower() == 'repeat':
        sock.close()
        tkinter.messagebox.showerror('很抱歉', '不允许重复报到')
        return
    elif data.lower() == 'notmatch':
        sock.close()
        tkinter.messagebox.showerror('很抱歉', '学号与姓名不匹配')
        return
    elif data.lower() == 'numberenough':
        sock.close()
        tkinter.messagebox.showerror('很抱歉', '同一机器点名人数不得大于分组人数')
        return
    else:
        sock.close()
        tkinter.messagebox.showerror('很抱歉', 'data不可识别')
        return

buttonOk = tkinter.Button(root, text='签到',font=fonttype,background=backtype,   command=buttonOKClick)
buttonOk.place(x=20, y=90, width=80, height=20)

##===============================签到功能代码结束=============================##






# ==========================提问功能模块======================================##
int_windowTiwen = tkinter.IntVar(root, value=0)
string_wenti = tkinter.StringVar(root, value='')


class windowTiwen:
    def __init__(self, root):
        string_wenti.set('')
        # 创建面板容器，用于放置其他控件
        self.top = tkinter.Toplevel(root, width=300, height=180)
        self.top.resizable(False, False)
        self.top.title('请输入你的问题')
        self.top.attributes('-topmost', 1)
        entryMessage = tkinter.scrolledtext.ScrolledText(self.top, wrap=tkinter.WORD)  # 创建一个带滚动条的文本框
        entryMessage.place(x=10, y=20, width=280, height=100)

        def buttonTiwenClick():
            # 回复，解答
            wenti = entryMessage.get(0.0, tkinter.END)

            string_wenti.set(wenti.strip())
            self.top.destroy()

        buttonTiwen = tkinter.Button(self.top, text='确定提问',command=buttonTiwenClick)
        buttonTiwen.place(x=90, y=130, width=80, height=20)


def buttonTiwenClick():
    if int_windowTiwen.get() == 1:
        return

    xuehao = entryXuehao.get()  # 获取用户名
    xingming = entryXingming.get()  # 获取密码
    serverIP = entryServerIP.get()

    if not re.match('^(\d){1,3}\.(\d){1,3}\.(\d){1,3}\.(\d){1,3}$', serverIP):
        tkinter.messagebox.showerror('很抱歉', '服务器IP地址不合法')
        return

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.connect((serverIP, 20000))
    except Exception as e:
        tkinter.messagebox.showerror('很抱歉', '现在不允许提问！')
        return

    int_windowTiwen.set(1)
    w = windowTiwen(root)
    buttonTiwen.wait_window(w.top)
    int_windowTiwen.set(0)
    if string_wenti.get() != '':
        # 如果确实有问题，则向服务器端发送
        message = xuehao + ',' + xingming + ':' + string_wenti.get()
        sock.sendall(message.encode())
        data = sock.recv(1024)
        data = data.decode().strip().lower()  ##去空，转换所有大写字母为小写
        if data == 'notmatch':
            tkinter.messagebox.showerror('很抱歉', '学号与姓名不匹配')
            sock.close()
            return
        elif data == 'wait':
            tkinter.messagebox.showinfo('稍等', '该问题稍后我会统一进行讲解！')
            sock.close()
            return
        elif data == 'close':
            tkinter.messagebox.showinfo('稍等', '老师正在忙碌中，请稍等一会儿再发送')
            sock.close()
            return
        else:
            tkinter.messagebox.showinfo('老师回复', data)
            sock.close()
            return
    else:
        sock.sendall('over'.encode())
        sock.close()


buttonTiwen = tkinter.Button(root, text='提问',font=fonttype,background=backtype, command=buttonTiwenClick)
buttonTiwen.place(x=120, y=90, width=80, height=20)
##======================================提问功能结束=========================================##




##===================================发送与截图功能代码======================================##

class MyCapture:

    def __init__(self, png,fileName):

        #变量X和Y用来记录鼠标左键按下的位置

        self.X = tkinter.IntVar(value=0)

        self.Y = tkinter.IntVar(value=0)

        #屏幕尺寸

        screenWidth =root.winfo_screenwidth()

        screenHeight =root.winfo_screenheight()

        #创建顶级组件容器
        im = Image.open(png)
        w = im.width  # 图片的宽
        h = im.height  # 图片的高
        out =im.resize((screenWidth, screenHeight), Image.ANTIALIAS)  # resize image with high-quality
        out.save(png)
        self.top = tkinter.Toplevel(root, width=screenWidth, height=screenHeight)
        #tkinter.messagebox.showinfo('aaa',str(w)+','+str(h))
        #不显示最大化、最小化按钮

        self.top.overrideredirect(True)

        self.canvas = tkinter.Canvas(self.top,bg='white', width=screenWidth, height=screenHeight)

        #显示全屏截图，在全屏截图上进行区域截图

        self.image = tkinter.PhotoImage(file=png)

        self.canvas.create_image(0 , 0,anchor = 'nw',image = self.image)

        #鼠标左键按下的位置
        def onLeftButtonDown(event):

            self.X.set(event.x)

            self.Y.set(event.y)

            #开始截图

            self.sel = True

        self.canvas.bind('<Button-1>', onLeftButtonDown)

        #鼠标左键移动，显示选取的区域

        def onLeftButtonMove(event):

            if not self.sel:

                return

            global lastDraw

            try:

                #删除刚画完的图形，要不然鼠标移动的时候是黑乎乎的一片矩形

                self.canvas.delete(lastDraw)

            except Exception as e:

                pass

            lastDraw = self.canvas.create_rectangle(self.X.get(), self.Y.get(), event.x , event.y, outline='black')

        self.canvas.bind('<B1-Motion>', onLeftButtonMove)

        #获取鼠标左键抬起的位置，保存区域截图

        def onLeftButtonUp(event):

            self.sel = False

            try:

                self.canvas.delete(lastDraw)

            except Exception as e:

                pass

            sleep(0.1)

            #考虑鼠标左键从右下方按下而从左上方抬起的截图

            left, right = sorted([self.X.get(), event.x])       #排序默认从小到大

            top, bottom = sorted([self.Y.get(), event.y])

            pic = ImageGrab.grab((left*w/screenWidth, top*h/screenHeight, right*w/screenWidth, bottom*h/screenHeight))


            k=tkinter.messagebox.askyesno('截图','是否保存截图并提交上传?')
            if k == True:
                pic.save(fileName,quality=100)
            else:
                return

         #关闭当前窗口

            self.top.destroy()

        self.canvas.bind('<ButtonRelease-1>', onLeftButtonUp)

        #让canvas充满窗口，并随窗口自动适应大小

        self.canvas.pack(fill=tkinter.BOTH, expand=tkinter.YES)

def buttonZuoyeClick():
    xuehao = entryXuehao.get()  # 获取用户名
    xingming = entryXingming.get()  # 获取密码
    serverIP = entryServerIP.get()
    if not re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', serverIP):
        tkinter.messagebox.showerror('很抱歉', '服务器IP地址不合法')
        return

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.connect((serverIP, 30100))
    except Exception as e:
        tkinter.messagebox.showerror('很抱歉', '现在不是交作业时间')
        return

    filename = xuehao + '_' + xingming + '.png'
    #im = ImageGrab.grab()
    ##im.save(filename)
    #im.close()

    # 最小化主窗口
    root.state('icon')

    sleep(0.2)

    filename1 = 'temp.png'

    # grab()方法默认对全屏幕进行截图

    im = ImageGrab.grab()

    im.save(filename1)

    im.close()

    # 显示全屏幕截图

    w = MyCapture(filename1,filename)

    buttonZuoye.wait_window(w.top)

    # 截图结束，恢复主窗口，并删除临时的全屏幕截图文件

    root.state('normal')

    os.remove(filename1)

    BUFSIZE = 1024
    FILEINFO_SIZE = struct.calcsize('I128sI')

    # 开始发送截图
    fhead = struct.pack('I128sI', len(filename), filename.encode(), os.stat(filename).st_size)
    sock.send(fhead)  # 发送文件名和大小等信息
    data = sock.recv(1024)  # 接收服务器反馈
    data = data.decode()
    if data.lower() == 'notmatch':
        tkinter.messagebox.showerror('很抱歉', '学号与姓名不匹配')
        sock.close()
        return

    fp = open(filename, 'rb')
    while True:
        filedata = fp.read(BUFSIZE)
        if not filedata:
            break
        sock.send(filedata)
    fp.close()
    sock.close()
    tkinter.messagebox.showinfo('恭喜', '交作业成功')


buttonZuoye = tkinter.Button(root, text='截图并上传',font=fonttype,background=backtype, command=buttonZuoyeClick)
buttonZuoye.place(x=220, y=90, width=90, height=20)


##===============================截图功能代码结束===========================##


# ================上传文件交作业，服务端应监听10500端口=========================##
def buttonShangchuanWenjianJiaozuoyeClick():
    xuehao = entryXuehao.get()  # 获取用户名
    xingming = entryXingming.get()  # 获取密码
    serverIP = entryServerIP.get()
    if not re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', serverIP):
        tkinter.messagebox.showerror('很抱歉', '服务器IP地址不合法')
        return

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.connect((serverIP, 10500))
    except Exception as e:
        tkinter.messagebox.showerror('很抱歉', '现在不是交作业时间')
        return

    filename = tkinter.filedialog.askopenfilename(title='请选择要上传的文件',
                                                  filetypes=[ ('Zip files', '*.zip'),
                                                             ('Rar files', '*.rar')
                                                            ])
    if filename:
        BUFSIZE = 1024
        FILEINFO_SIZE = struct.calcsize('I128sI')
        temp = xuehao + '_' + xingming + '_1_' + filename
        fhead = struct.pack('I128sI', len(temp), temp.encode(), os.stat(filename).st_size)
        sock.send(fhead)  # 发送文件名和大小等信息
        data = sock.recv(1024)  # 接收服务器反馈
        # 检查学生填写的学号和姓名是否匹配
        data = data.decode()
        if data.lower() == 'notmatch':
            tkinter.messagebox.showerror('很抱歉', '学号与姓名不匹配')
            sock.close()
            return

        with open(filename, 'rb') as fp:
            sock.send(fp.read())
        sock.close()
        tkinter.messagebox.showinfo('恭喜', '交作业成功')


buttonShangchuanWenjianJiaozuoye = tkinter.Button(root,
                                                  text='上传文件',font=fonttype,background=backtype,
                                                  command=buttonShangchuanWenjianJiaozuoyeClick)
buttonShangchuanWenjianJiaozuoye.place(x=20, y=120, width=80, height=20)


#==========================================上传文件作业代码结束=======================================##

#=====================================下载课程说明文件================================================##

def  donwnload_thread():
    serverIP = entryServerIP.get()
    if not re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', serverIP):
        tkinter.messagebox.showerror('很抱歉', '服务器IP地址不合法')
        return

    serverIP = '"C:\Program Files\internet explorer\iexplore.exe "' + ' http://' + serverIP + ':8880/共享文件夹'
    try:
        import subprocess

        cmd = serverIP
        res = subprocess.call(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    except :
        tkinter.messagebox.showerror('很抱歉', '服务器端未开启下载！')



def  buttonXiazaiWenjianJiaozuoyeOnclick() :
        t = threading.Thread(target=donwnload_thread)
        # 启动下载文件线程
        t.start()



buttonXiazaiWenjianJiaozuoye = tkinter.Button(root,
                                                  text='下载帮助文件',font=fonttype,background=backtype,
                                                  command=buttonXiazaiWenjianJiaozuoyeOnclick)
buttonXiazaiWenjianJiaozuoye.place(x=120, y=120, width=80, height=20)
##=====================================下载文件===========================================================##



# ==============================================接收屏幕广播功能代码==============================================##

# 使用TCP接收广播
def receiveBroadCast():
    # 获取屏幕尺寸，创建铺满屏幕的无标题栏窗体
    screenWidth =  400#root.winfo_screenwidth()
    screenHeight = 300# root.winfo_screenheight()
    top = tkinter.Toplevel(root,
                           width=screenWidth,
                           height=screenHeight)
    top.overrideredirect(True)
    # 顶端显示
    top.attributes('-topmost', 1)
    # 创建画布，用来显示图像
    canvas = tkinter.Canvas(top,
                            bg='white',
                            width=screenWidth,
                            height=screenHeight)
    canvas.pack(fill=tkinter.BOTH, expand=tkinter.YES)

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serverIP = entryServerIP.get()
    # 连接服务器10001端口，失败直接返回
    try:
        sock.connect((serverIP, 10001))
    except:
        print('error')
        top.destroy()
        return

    # 接收服务器指令
    # *****表示开始传输一个新的截图
    # #####表示本次广播结束
    while True:
        data = sock.recv(5)
        if data == b'*****':
            # 接收服务器发来的一屏图像
            # 图像大小，字节总数量
            len_head = struct.calcsize('I32sI')
            data = sock.recv(len_head)
            length, size, sizeLength = struct.unpack('I32sI', data)
            length = int(length)
            size = eval(size[:int(sizeLength)])

            rest = length
            image = []
            while True:
                if rest == 0:
                    break
                elif rest > 40960:
                    temp = sock.recv(40960)
                    rest -= len(temp)
                    image.append(temp)
                else:
                    temp = sock.recv(rest)
                    rest -= len(temp)
                    image.append(temp)
            image = b''.join(image)
            # 更新显示
            image = Image.frombytes('RGB', size, image)
            image = image.resize((screenWidth, screenHeight))
            image = ImageTk.PhotoImage(image)
            imageId = canvas.create_image(screenWidth // 2, screenHeight // 2, image=image)
        elif data == b'#####':
            # 广播结束
            break

    # 本次广播结束，关闭窗口
    sock.close()
    top.destroy()



## =============================屏幕广播代码结束=============================================##

# ========================自动更新服务器地址及广播监听=======================##

int_searchServer = tkinter.IntVar(root, value=1)

# 使用UDP监听，等待服务器的广播指令
def udpListen():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 监听本机10000端口
    sock.bind(('', 10000))
    while int_searchServer.get()==1:
        data, addr = sock.recvfrom(100)
        # 收到服务器发来的广播指令
        if data == b'startBroadCast':
            threading.Thread(target=receiveBroadCast).start()
        elif data.decode() == 'ServerIP':  # 输出收到的信息
            server_IP.set(addr[0])
            time.sleep(0.3)
        elif  entryXingming.get() in data.decode() :
            try:
                chengyaunming,xingming=data.decode().split('0')
                tkinter.messagebox.showinfo('提问提示', '恭喜你' + xingming + ',你被抽中了！')
            except :
                pass
        elif data == b'close':
            if int_searchServer.get() == 1:
                int_searchServer.set(0)

            root.destroy()
        else:
            pass
    sock.close()


Uplistenthreading=threading.Thread(target=udpListen)
Uplistenthreading.daemon = True
Uplistenthreading.start()



##================================自动监听代码结束================================##



##==================================关闭窗口===================##
def closeWindow():
    if int_searchServer.get() == 1:
        int_searchServer.set(0)
        time.sleep(0.2)
    root.destroy()

root.protocol('WM_DELETE_WINDOW', closeWindow)
#===================窗口关闭===================##



root.mainloop()  # 启动消息循环
